home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / storage / ipc / shmem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  15.0 KB  |  555 lines

  1. /*
  2.  * shmem.c -- create shared memory and initialize shared memory
  3.  *    data structures.
  4.  *
  5.  * Identification:
  6.  *    $Header: /private/postgres/src/storage/ipc/RCS/shmem.c,v 1.10 1991/11/14 19:39:33 kemnitz Exp $
  7.  *
  8.  * POSTGRES processes share one or more regions of shared memory.
  9.  * The shared memory is created by a postmaster and is "attached to"
  10.  * by each of the backends.  The routines in this file are used for
  11.  * allocating and binding to shared memory data structures.
  12.  *
  13.  * NOTES:
  14.  *    (a) There are three kinds of shared memory data structures
  15.  *  available to POSTGRES: fixed-size structures, queues and hash 
  16.  *  tables.  Fixed-size structures contain things like global variables
  17.  *  for a module and should never be allocated after the process 
  18.  *  initialization phase.  Hash tables have a fixed maximum size, but
  19.  *  their actual size can vary dynamically.  When entries are added
  20.  *  to the table, more space is allocated.  Queues link data structures 
  21.  *  that have been allocated either as fixed size structures or as hash 
  22.  *  buckets.  Each shared data structure has a string name to identify 
  23.  *  it (assigned in the module that declares it).
  24.  *
  25.  *    (b) During initialization, each module looks for its
  26.  *  shared data structures in a hash table called the "Binding Table".
  27.  *  If the data structure is not present, the caller can allocate
  28.  *  a new one and initialize it.  If the data structure is present, 
  29.  *  the caller "attaches" to the structure by initializing a pointer 
  30.  *  in the local address space.  
  31.  *     The binding table has two purposes: first, it gives us
  32.  *  a simple model of how the world looks when a backend process 
  33.  *  initializes.  If something is present in the binding table,
  34.  *  it is initialized.  If it is not, it is uninitialized.  Second,
  35.  *  the binding table allows us to allocate shared memory on demand
  36.  *  instead of trying to preallocate structures and hard-wire the
  37.  *  sizes and locations in header files.  If you are using a lot
  38.  *  of shared memory in a lot of different places (and changing
  39.  *  things during development), this is important.
  40.  *
  41.  *    (c) memory allocation model: shared memory can never be 
  42.  *  freed, once allocated.   Each hash table has its own free list,
  43.  *  so hash buckets can be reused when an item is deleted.  However,
  44.  *  if one hash table grows very large and then shrinks, its space
  45.  *  cannot be redistributed to other tables.  We could build a simple
  46.  *  hash bucket garbage collector if need be.  Right now, it seems
  47.  *  unnecessary.
  48.  *
  49.  *       See InitSem() in sem.c for an example of how to use the
  50.  *  binding table.
  51.  *
  52.  */
  53.  
  54. #include "tmp/postgres.h"
  55. #include "storage/ipc.h"
  56. #include "storage/ipci.h"
  57. #include "storage/shmem.h"
  58. #include "storage/spin.h"
  59. #include "utils/hsearch.h"
  60. #include "utils/log.h"
  61.  
  62. /* shared memory global variables */
  63.     /* start and end address of shared memory */
  64. unsigned int  ShmemBase = 0;
  65. unsigned int  ShmemEnd = 0;
  66.     /* current size (and default) */
  67. unsigned int  ShmemSize = 0;
  68.  
  69.     /* pointer to the OFFSET of first free shared memory */
  70. unsigned int *ShmemFreeStart = NULL;
  71.     /* start of the binding table (for bootstrap) */
  72. unsigned int *ShmemBindingTabOffset = NULL;
  73.     /* flag becomes true when shared mem is created by POSTMASTER*/
  74. int          ShmemBootstrap = FALSE;
  75.     /* lock for shared memory allocation */
  76. SPINLOCK      ShmemLock;
  77.     /* lock for binding table access */
  78. SPINLOCK      BindingLock;
  79.  
  80. /* forward */
  81. HTAB *ShmemInitHash();
  82.  
  83. HTAB *BindingTable = NULL;
  84.  
  85. /* ---------------------
  86.  * ShmemBindingTabReset() - Resets the binding table to NULL....
  87.  * useful when the postmaster destroys existing shared memory
  88.  * and creates all new segments after a backend crash.
  89.  * ----------------------
  90.  */
  91. ShmemBindingTabReset()
  92. {
  93.     BindingTable = (HTAB *)NULL;
  94. }
  95.  
  96. /*
  97.  *  CreateSharedRegion() --
  98.  *
  99.  *  This routine is called once by the postmaster to
  100.  *  initialize the shared buffer pool.  Assume there is
  101.  *  only one postmaster so no synchronization is necessary
  102.  *  until after this routine completes successfully.
  103.  *
  104.  * key is a unique identifier for the shmem region.
  105.  * size is the size of the region.
  106.  */
  107. static IpcMemoryId ShmemId;
  108.  
  109. ShmemCreate(key,size)
  110. unsigned int    key,size;
  111. {
  112.   if (size)
  113.       ShmemSize = size;
  114.   /* create shared mem region */
  115.   if ((ShmemId=IpcMemoryCreate(key,ShmemSize,IPCProtection))
  116.       ==IpcMemCreationFailed) {
  117.     elog(FATAL,"ShmemCreate: cannot create region");
  118.     exit(1);
  119.   }
  120.  
  121.   /* ShmemBootstrap is true if shared memory has been
  122.    * created, but not yet initialized.  Only the 
  123.    * postmaster/creator-of-all-things should have
  124.    * this flag set.
  125.    */
  126.   ShmemBootstrap = TRUE;
  127. }
  128.  
  129. /*
  130.  *  InitShmem() -- map region into process address space
  131.  *    and initialize shared data structures.
  132.  *
  133.  */
  134. InitShmem(key,size)
  135. unsigned int key;
  136. unsigned int size;
  137. {
  138.   Addr     sharedRegion;
  139.   unsigned int     currFreeSpace;
  140.  
  141.   HASHCTL     info;
  142.   int         hash_flags;
  143.   BindingEnt *    result,item;
  144.   Boolean    found;
  145.   IpcMemoryId    shmid;
  146.  
  147.   /* if zero key, use default memory size */
  148.   if (size)
  149.     ShmemSize = size;
  150.  
  151.   /* default key is 0 */
  152.  
  153.   /* attach to shared memory region (SysV or BSD OS specific) */
  154.   if (ShmemBootstrap && key == PrivateIPCKey)
  155.      /* if we are running backend alone */
  156.       shmid = ShmemId;
  157.   else
  158.       shmid = IpcMemoryIdGet(IPCKeyGetBufferMemoryKey(key), ShmemSize);
  159.   sharedRegion = IpcMemoryAttach(shmid);
  160.   if (sharedRegion == NULL) {
  161.     elog(FATAL,"AttachSharedRegion: couldn't attach to shmem\n");
  162.     return(FALSE);
  163.   }
  164.  
  165.   /* get pointers to the dimensions of shared memory */
  166.   ShmemBase = (unsigned int) sharedRegion;
  167.   ShmemEnd  = (unsigned int) sharedRegion + ShmemSize;
  168.   currFreeSpace = 0;
  169.  
  170.   /* First int in shared memory is the count of available space */
  171.   ShmemFreeStart = (unsigned int *) ShmemBase;
  172.   /* next is a shmem pointer to the binding table */
  173.   ShmemBindingTabOffset = ShmemFreeStart + 1;
  174.  
  175.   currFreeSpace += 
  176.     sizeof(ShmemFreeStart) + sizeof(ShmemBindingTabOffset);
  177.  
  178.   /* bootstrap initialize spin locks so we can start to use the
  179.    * allocator and binding table.
  180.    */
  181.   if (! InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) {
  182.     return(FALSE);
  183.   }
  184.  
  185.   /* We have just allocated additional space for two spinlocks.
  186.    * Now setup the global free space count 
  187.    */
  188.   if (ShmemBootstrap) {
  189.     *ShmemFreeStart = currFreeSpace;
  190.   }
  191.  
  192.   /* if ShmemFreeStart is NULL, then the allocator won't work */
  193.   Assert(*ShmemFreeStart);
  194.  
  195.   /* create OR attach to the shared memory binding table */
  196.   info.keysize = BTABLE_KEYSIZE;
  197.   info.datasize = BTABLE_DATASIZE;
  198.   hash_flags = (HASH_ELEM);
  199.  
  200.   /* This will acquire the binding table lock, but not release it. */
  201.   BindingTable = ShmemInitHash("BindingTable",
  202.                    BTABLE_SIZE,BTABLE_SIZE,
  203.                    &info,hash_flags);
  204.  
  205.   if (! BindingTable) {
  206.     elog(FATAL,"InitShmem: couldn't initialize Binding Table");
  207.     return(FALSE);
  208.   }
  209.  
  210.   /* Now, check the binding table for an entry to the binding
  211.    * table.  If there is an entry there, someone else created
  212.    * the table.  Otherwise, we did and we have to initialize it.
  213.    */
  214.   bzero(item.key,BTABLE_KEYSIZE);
  215.   strncpy(item.key,"BindingTable",BTABLE_KEYSIZE);
  216.  
  217.   result = (BindingEnt *) 
  218.     hash_search(BindingTable,(char *) &item,HASH_ENTER, &found);
  219.  
  220.  
  221.   if (! result ) {
  222.     elog(FATAL,"InitShmem: corrupted binding table");
  223.     return(FALSE);
  224.   }
  225.  
  226.   if (! found) {
  227.     /* bootstrapping shmem: we have to initialize the 
  228.      * binding table now.
  229.      */
  230.  
  231.     Assert(ShmemBootstrap);
  232.     result->location = MAKE_OFFSET(BindingTable->hctl);
  233.     *ShmemBindingTabOffset = result->location;
  234.     result->size = BTABLE_SIZE;
  235.  
  236.     ShmemBootstrap = FALSE;
  237.  
  238.   }  else {
  239.     Assert(! ShmemBootstrap);
  240.   }
  241.   /* now release the lock acquired in ShmemHashInit */
  242.   SpinRelease (BindingLock);
  243.  
  244.   Assert (result->location == MAKE_OFFSET(BindingTable->hctl));
  245.  
  246.   return(TRUE);
  247. }
  248.  
  249. /*
  250.  * ShmemAlloc -- allocate word-aligned byte string from
  251.  *     shared memory
  252.  *
  253.  * Assumes ShmemLock and ShmemFreeStart are initialized.
  254.  * Returns: real pointer to memory or NULL if we are out
  255.  *     of space.  Has to return a real pointer in order 
  256.  *      to be compatable with malloc().
  257.  */
  258. int *
  259. ShmemAlloc(size)
  260. unsigned int size;
  261. {
  262.   unsigned int tmpFree;
  263.   int *newSpace;
  264.  
  265.   /* ensure space is word aligned */
  266.   if (size % sizeof(int *))
  267.     size += sizeof(int *) - (size % sizeof(int *));
  268.  
  269.   Assert(*ShmemFreeStart);
  270.  
  271.   SpinAcquire(ShmemLock);
  272.  
  273.   tmpFree = *ShmemFreeStart + size;
  274.   if (tmpFree <= ShmemSize) {
  275.     newSpace = (int *)MAKE_PTR(*ShmemFreeStart);
  276.     *ShmemFreeStart += size;
  277.   } else {
  278.     newSpace = NULL;
  279.   }
  280.  
  281.   SpinRelease(ShmemLock); 
  282.  
  283.   if (! newSpace) {
  284.     elog(NOTICE,"ShmemAlloc: out of memory ");
  285.   }
  286.   return(newSpace);
  287. }
  288.  
  289. /*
  290.  * ShmemIsValid -- test if an offset refers to valid shared memory 
  291.  * 
  292.  * Returns TRUE if the pointer is valid.
  293.  */
  294. ShmemIsValid(addr)
  295. unsigned int addr;
  296. {
  297.   return ((addr<ShmemEnd) && (addr>=ShmemBase));
  298. }
  299.  
  300. /*
  301.  * ShmemInitHash -- Create/Attach to and initialize 
  302.  *     shared memory hash table.
  303.  *
  304.  * Notes:
  305.  *
  306.  * assume caller is doing some kind of synchronization
  307.  * so that two people dont try to create/initialize the
  308.  * table at once.  Use SpinAlloc() to create a spinlock
  309.  * for the structure before creating the structure itself.
  310.  */
  311. HTAB *
  312. ShmemInitHash(name,init_size,max_size,infoP,hash_flags)
  313. char *    name;        /* table string name for binding */
  314. int     init_size;    /* initial size */
  315. int     max_size;    /* max size of the table */
  316. HASHCTL *infoP;        /* info about key and bucket size */
  317. int hash_flags;        /* info about infoP */
  318. {
  319.   Boolean    found;
  320.   int  *    location;
  321.  
  322.   /* shared memory hash tables have a fixed max size so that the
  323.    * control structures don't try to grow.  The segbase is for
  324.    * calculating pointer values.  The shared memory allocator
  325.    * must be specified.
  326.    */
  327.   infoP->segbase = (int *) ShmemBase;
  328.   infoP->alloc = ShmemAlloc;
  329.   infoP->max_size = max_size;
  330.   hash_flags |= HASH_SHARED_MEM;
  331.  
  332.   /* look it up in the binding table */
  333.   location = 
  334.     ShmemInitStruct(name,my_log2(max_size) + sizeof(HHDR),&found);
  335.  
  336.   /* binding table is corrupted.  Let someone else give the 
  337.    * error message since they have more information 
  338.    */
  339.   if (location == NULL) {
  340.     return(0);
  341.   }
  342.  
  343.   /* it already exists, attach to it rather than allocate and
  344.    * initialize new space 
  345.    */
  346.   if (found) {
  347.     hash_flags |= HASH_ATTACH;
  348.   }
  349.  
  350.   /* these structures were allocated or bound in ShmemInitStruct */
  351.     /* control information and parameters */
  352.   infoP->hctl = (int *) location;
  353.     /* directory for hash lookup */
  354.   infoP->dir = (int *) (location + sizeof(HHDR));
  355.   
  356.   return(hash_create(init_size, infoP, hash_flags));;
  357. }
  358.  
  359. /*
  360.  * ShmemPIDLookup -- lookup process data structure using process id
  361.  *
  362.  * Returns: TRUE if no error.  locationPtr is initialized if PID is
  363.  *    found in the binding table.
  364.  *
  365.  * NOTES:
  366.  *     only information about success or failure is the value of
  367.  *    locationPtr.
  368.  */
  369. Boolean
  370. ShmemPIDLookup(pid,locationPtr)
  371. int        pid;
  372. SHMEM_OFFSET*    locationPtr;
  373. {
  374.   BindingEnt *    result,item;
  375.   Boolean    found;
  376.  
  377.   Assert (BindingTable);
  378.   bzero(item.key,BTABLE_KEYSIZE);
  379.   sprintf(item.key,"PID %d",pid);
  380.   
  381.   SpinAcquire(BindingLock);
  382.   result = (BindingEnt *) 
  383.     hash_search(BindingTable,(char *) &item, HASH_ENTER, &found);
  384.  
  385.   if (! result) {
  386.  
  387.     SpinRelease(BindingLock);
  388.     elog(WARN,"ShmemInitPID: BindingTable corrupted");
  389.     return(FALSE);
  390.  
  391.   } 
  392.  
  393.   if (found) {
  394.     *locationPtr = result->location;
  395.   } else {
  396.     result->location = *locationPtr;
  397.   }
  398.  
  399.   SpinRelease(BindingLock);
  400.   return (TRUE);
  401. }
  402.  
  403. /*
  404.  * ShmemPIDDestroy -- destroy binding table entry for process
  405.  *    using process id
  406.  *
  407.  * Returns: offset of the process struct in shared memory or
  408.  *    INVALID_OFFSET if not found.
  409.  *
  410.  * Side Effect: removes the entry from the binding table
  411.  */
  412. SHMEM_OFFSET
  413. ShmemPIDDestroy(pid)
  414. int        pid;
  415. {
  416.   BindingEnt *    result,item;
  417.   Boolean    found;
  418.   SHMEM_OFFSET  location;
  419.  
  420.   Assert(BindingTable);
  421.  
  422.   bzero(item.key,BTABLE_KEYSIZE);
  423.   sprintf(item.key,"PID %d",pid);
  424.   
  425.   SpinAcquire(BindingLock);
  426.   result = (BindingEnt *) 
  427.     hash_search(BindingTable,(char *) &item, HASH_REMOVE, &found);
  428.  
  429.   if (found)
  430.     location = result->location;
  431.   SpinRelease(BindingLock);
  432.  
  433.   if (! result) {
  434.  
  435.     elog(WARN,"ShmemPIDDestroy: PID table corrupted");
  436.     return(INVALID_OFFSET);
  437.  
  438.   } 
  439.  
  440.   if (found)
  441.     return (location);
  442.   else {
  443.     return(INVALID_OFFSET);
  444.   }
  445. }
  446.  
  447. /*
  448.  * ShmemInitStruct -- Create/attach to a structure in shared
  449.  *     memory.
  450.  *
  451.  *  This is called during initialization to find or allocate
  452.  *         a data structure in shared memory.  If no other processes
  453.  *    have created the structure, this routine allocates space
  454.  *    for it.  If it exists already, a pointer to the existing
  455.  *     table is returned.  
  456.  *
  457.  *  Returns: real pointer to the object.  FoundPtr is TRUE if
  458.  *    the object is already in the binding table (hence, already
  459.  *    initialized).
  460.  */
  461. int *
  462. ShmemInitStruct(name,size,foundPtr)
  463. char *name;
  464. unsigned int size;
  465. Boolean *foundPtr;
  466. {
  467.   BindingEnt *    result,item;
  468.   int * structPtr;
  469.  
  470.   strncpy(item.key,name,BTABLE_KEYSIZE);
  471.   item.location = BAD_LOCATION;
  472.   
  473.   SpinAcquire(BindingLock);
  474.  
  475.   if (! BindingTable) {
  476.     /* Assert() is a macro now. substitutes inside quotes. */
  477.     char *strname = "BindingTable";
  478.  
  479.     /* If the binding table doesnt exist, we fake it.
  480.      *
  481.      * If we are creating the first binding table, then let 
  482.      * shmemalloc() allocate the space for a new HTAB.  Otherwise,
  483.      * find the old one and return that.  Notice that the
  484.      * BindingLock is held until the binding table has been completely
  485.      * initialized.
  486.      */
  487.     Assert (! strcmp(name,strname)) ;
  488.     if (ShmemBootstrap) {
  489.       /* in POSTMASTER/Single process */
  490.  
  491.       *foundPtr = FALSE;
  492.       return((int *)ShmemAlloc(size));
  493.  
  494.     } else {
  495.       Assert (ShmemBindingTabOffset);
  496.  
  497.       *foundPtr = TRUE;
  498.       return((int *)MAKE_PTR(*ShmemBindingTabOffset));
  499.     }
  500.  
  501.  
  502.   } else {
  503.     /* look it up in the bindint table */
  504.     result = (BindingEnt *) 
  505.       hash_search(BindingTable,(char *) &item,HASH_ENTER, foundPtr);
  506.   }
  507.  
  508.   if (! result) {
  509.  
  510.     SpinRelease(BindingLock);
  511.  
  512.     elog(WARN,"ShmemInitStruct: Binding Table corrupted");
  513.     return(NULL);
  514.  
  515.   } else if (*foundPtr) {
  516.     /*
  517.      * Structure is in the binding table so someone else has allocated 
  518.      * it already.  The size better be the same as the size we are 
  519.      * trying to initialize to or there is a name conflict (or worse).
  520.      */
  521.     if (result->size != size) {
  522.       SpinRelease(BindingLock);
  523.  
  524.       elog(NOTICE,"ShmemInitStruct: BindingTable entry size is wrong");
  525.       /* let caller print its message too */
  526.       return(NULL);
  527.     }
  528.     structPtr = (int *)MAKE_PTR(result->location);
  529.   } else {
  530.  
  531.     /* It isn't in the table yet. allocate and initialize it */
  532.     structPtr = ShmemAlloc(size);
  533.     if (! structPtr) {
  534.       /* out of memory */
  535.       Assert (BindingTable);
  536.       (void) hash_search(BindingTable,(char *) &item,HASH_REMOVE, foundPtr);
  537.       SpinRelease(BindingLock);
  538.       *foundPtr = FALSE;
  539.  
  540.       elog(NOTICE,"ShmemInitStruct: cannot allocate '%s'",
  541.        name);
  542.       return(NULL);
  543.     } 
  544.     result->size = size;
  545.     result->location = MAKE_OFFSET(structPtr);
  546.   }
  547.   Assert (ShmemIsValid((unsigned int)structPtr));
  548.  
  549.   SpinRelease(BindingLock);
  550.   return(structPtr);
  551. }
  552.  
  553.  
  554.  
  555.